/**
 * Created with CLion.
 * Description: 
 * User: loong
 * Date: 2024/3/27
 * Time: 21:37
 */

#include <atomic>
#include <iostream>
#include <memory>
#include <utility>
using namespace std;
struct Block {
    atomic_int use_c_  = 1;
    atomic_int weak_c_ = 1;
};

template<typename T>
class SharedPtr
{
    friend class SharedPtr;

public:
    SharedPtr()
    {
    }

    template<typename U>
    explicit SharedPtr(U* ptr)
    {
        ptr_ = ptr;
        req_ = new Block;
    }

    ~SharedPtr()
    {
        if(nullptr != req_) {
            if(--req_->use_c_ == 0) {
                delete ptr_;

                if(--req_->weak_c_ == 0)
                    delete req_;
            }
        }
    }

    SharedPtr(const SharedPtr& other)
    {
        this->Copy_construct_from(other);
    }

    SharedPtr(SharedPtr&& other)
    {
        this->Move_construct_from(std::move(other));
    }

    SharedPtr& operator=(const SharedPtr& other)
    {
        SharedPtr(other).swap(*this);
        return *this;
    }

    SharedPtr& operator=(SharedPtr&& _Right)
    {
        SharedPtr(std::move(_Right)).swap(*this);
        return *this;
    }

    void Reset()
    {
        SharedPtr().swap(*this);
    }

    T& operator*() const
    {
        return *ptr_;
    }

    T* operator->() const
    {
        return ptr_;
    }

    int UseCount() const
    {
        return nullptr == req_ ? 0 : static_cast<int>(req_->use_c_);
    }

    bool Unique() const
    {
        return UseCount() == 1;
    }

    explicit operator bool() const
    {
        return nullptr != ptr_;
    }

    void swap(SharedPtr& other)
    {
        using namespace std;
        std::swap(ptr_, other.ptr_);
        std::swap(req_, other.req_);
    }

private:
    template<class Ty2>
    void Move_construct_from(SharedPtr<Ty2>&& other)
    {
        ptr_ = other.ptr_;
        req_ = other.req_;

        other.ptr_ = nullptr;
        other.req_ = nullptr;
    }

    template<class Ty2>
    void Copy_construct_from(const SharedPtr<Ty2>& other)
    {
        if(other.req_) {
            ++other.req_->use_c_;
        }

        ptr_ = other.ptr_;
        req_ = other.req_;
    }

    template <class T>
    friend class WeakPtr;

    template<class _Ty2>
    bool Construct_from_weak(const WeakPtr<_Ty2>& other)
    {
        if(nullptr != other.req_) {
            ptr_ = other.ptr_;
            req_ = other.req_;
            return true;
        }

        return false;
    }
private:
    T*     ptr_ = nullptr;
    Block* req_ = nullptr;
};

template<typename T>
class WeakPtr
{
public:
    WeakPtr() {}

    WeakPtr(const WeakPtr& other)
    {
        this->Weakly_construct_from(other);
    }

    WeakPtr(WeakPtr&& other)
    {
        this->Move_construct_from(std::move(other));
    }

    WeakPtr(const SharedPtr<T> & other) noexcept {
        this->Weakly_construct_from(other);
    }

    template <class _Ty2>
    WeakPtr& operator=(const SharedPtr<_Ty2>& other) noexcept {
        WeakPtr(other).swap(*this);
        return *this;
    }

    ~WeakPtr()
    {
        if(nullptr != req_) {
            if(--req_->weak_c_ == 0)
                delete req_;
        }
    }

    WeakPtr& operator=(const WeakPtr& other)
    {
        WeakPtr(other).swap(*this);
        return *this;
    }

    WeakPtr& operator=(WeakPtr&& other) noexcept
    {
        weak_ptr(std::move(other)).swap(*this);
        return *this;
    }

    void Reset()
    {
        WeakPtr().swap(*this);
    }

    bool expired() const noexcept
    {
        return this->UseCount() == 0;
    }

    SharedPtr<T> lock() const
    {
        SharedPtr<T> ret;
        if(nullptr != req_)
            (void) ret.Construct_from_weak(*this);
        return ret;
    }

    int UseCount() const
    {
        return nullptr == req_ ? 0 : static_cast<int>(req_->use_c_);
    }

    void swap(WeakPtr& other)
    {
        using namespace std;
        std::swap(ptr_, other.ptr_);
        std::swap(req_, other.req_);
    }

private:
    void Swap(WeakPtr& other)
    {
        using namespace std;
        std::swap(ptr_, other.ptr_);
        std::swap(req_, other.req_);
    }

    template<class _Ty2>
    void Weakly_construct_from(const WeakPtr<_Ty2>& other)
    {
        if(other.req_) {
            ptr_ = other.ptr_;
            req_ = other.req_;
            ++req_->weak_c_;
        }
    }

    template<class _Ty2>
    void Weakly_construct_from(const SharedPtr<_Ty2>& other)
    {
        if(other.req_) {
            ptr_ = other.ptr_;
            req_ = other.req_;
            ++req_->weak_c_;
        }
    }

    template<class Ty2>
    void Move_construct_from(WeakPtr<Ty2>&& other)
    {
        ptr_ = other.ptr_;
        req_ = other.req_;

        other.ptr_ = nullptr;
        other.req_ = nullptr;
    }

    friend SharedPtr<T>;
private:
    T*     ptr_ = nullptr;
    Block* req_ = nullptr;
};

struct Teacher;
struct Student
{
    int age = 15;
    WeakPtr<Teacher> tea;

    Student();
    ~Student();
    void bar();
};
struct Teacher
{
    int age = 30;
    SharedPtr<Student> stu;

    Teacher();
    ~Teacher();
    void bar();
};

Student::Student() { std::cout << "Student::Student\n"; }

Student::~Student() { std::cout << "Student::~Student\n"; }

void Student::bar() { std::cout << tea.lock()->age; }

Teacher::Teacher() { std::cout << "Teacher::Teacher\n"; }

Teacher::~Teacher() { std::cout << "Teacher::~Teacher\n"; }

void Teacher::bar() { std::cout << stu->age; }



void f(const SharedPtr<Student>& i)
{
    std::cout << "shared_ptr (use count " << i.UseCount() << ") to ";
    i->bar();
}

int main()
{
    // shared_ptr<Student> s;
    // // 构造了一个 std::shared_ptr
    // SharedPtr<Student>  sp1(new Student);
    //
    // // 使用了拷贝构造函数
    // SharedPtr<Student> sp2(sp1);
    //
    // // 使用了拷贝赋值操作符
    // SharedPtr<Student> sp3;
    // sp3 = sp2;
    //
    // // 调用了成员函数
    // std::cout << "Use count == " << sp1.UseCount() << '\n';
    // f(sp1);
    // f(sp2);
    // f(sp3);
    //
    // // 重置 shared_ptr，引用计数减少
    // sp1.Reset();
    // std::cout << "Use count == " << sp2.UseCount() << '\n';

    // 最后一个 shared_ptr 被销毁，对象被删除

    SharedPtr<Teacher> t(new Teacher);
    t = t;
    SharedPtr<Student> s(new Student);

    t->stu = s;
    s->tea = t;
    return 0;
}


